package com.jboltai.pptextract;

import org.apache.poi.xslf.usermodel.*;
import org.apache.poi.hslf.usermodel.*;
import org.apache.poi.sl.usermodel.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.util.*;
import org.apache.poi.sl.usermodel.PictureData.PictureType;
import org.apache.poi.xslf.usermodel.XSLFPictureData;
import org.apache.poi.hslf.usermodel.HSLFPictureData;
import org.apache.poi.ooxml.POIXMLDocumentPart;
import org.apache.poi.openxml4j.opc.PackagePart;

/**
 * Implementation of PPTPageProcessor using Apache POI
 */
public class PPTPageProcessorImpl implements PPTPageProcessor {
    private static final Logger logger = LoggerFactory.getLogger(PPTPageProcessorImpl.class);

    @Override
    public void processSlideBySlide(File pptFile, File outputDir, SlideContentHandler handler) throws IOException {
        String fileName = pptFile.getName().toLowerCase();

        // Create output directory if it doesn't exist
        if (!outputDir.exists()) {
            outputDir.mkdirs();
        }

        if (fileName.endsWith(".pptx")) {
            processXSLFSlideBySlide(pptFile, outputDir, handler);
        } else if (fileName.endsWith(".ppt")) {
            processHSLFSlideBySlide(pptFile, outputDir, handler);
        } else {
            throw new IllegalArgumentException("Unsupported file format: " + fileName);
        }
    }

    @Override
    public int getSlideCount(File pptFile) throws IOException {
        String fileName = pptFile.getName().toLowerCase();

        if (fileName.endsWith(".pptx")) {
            try (XMLSlideShow ppt = new XMLSlideShow(new FileInputStream(pptFile))) {
                return ppt.getSlides().size();
            }
        } else if (fileName.endsWith(".ppt")) {
            try (HSLFSlideShow ppt = new HSLFSlideShow(new FileInputStream(pptFile))) {
                return ppt.getSlides().size();
            }
        } else {
            throw new IllegalArgumentException("Unsupported file format: " + fileName);
        }
    }

    private void processXSLFSlideBySlide(File pptFile, File outputDir, SlideContentHandler handler) throws IOException {
        try (XMLSlideShow ppt = new XMLSlideShow(new FileInputStream(pptFile))) {
            List<XSLFSlide> slides = ppt.getSlides();
            int slideNumber = 1;

            // Create a map to store all picture data for reference
            Map<String, XSLFPictureData> allPictureData = new HashMap<>();
            for (XSLFPictureData pictureData : ppt.getPictureData()) {
                allPictureData.put(pictureData.getFileName(), pictureData);
            }

            for (XSLFSlide slide : slides) {
                // Extract slide title
                String title = getSlideTitle(slide);
                
                // Extract text content
                StringBuilder textContent = new StringBuilder();
                for (XSLFShape shape : slide.getShapes()) {
                    if (shape instanceof XSLFTextShape) {
                        XSLFTextShape textShape = (XSLFTextShape) shape;
                        String text = textShape.getText();
                        if (text != null && !text.trim().isEmpty() && 
                            (title == null || !text.equals(title))) {
                            textContent.append(text).append("\n\n");
                        }
                    }
                }

                // Extract media files for this slide
                Map<String, File> slideMediaFiles = extractMediaFromXSLFSlide(slide, outputDir, slideNumber, allPictureData);

                // Call the handler with the slide content
                handler.onSlideProcessed(
                    slideNumber,
                    title,
                    textContent.toString().trim(),
                    slideMediaFiles
                );

                slideNumber++;
            }
        }
    }

    private void processHSLFSlideBySlide(File pptFile, File outputDir, SlideContentHandler handler) throws IOException {
        try (HSLFSlideShow ppt = new HSLFSlideShow(new FileInputStream(pptFile))) {
            List<HSLFSlide> slides = ppt.getSlides();
            int slideNumber = 1;

            // Create a map to store all picture data for reference
            Map<String, HSLFPictureData> allPictureData = new HashMap<>();
            for (HSLFPictureData pictureData : ppt.getPictureData()) {
                // Use index as key since HSLF doesn't provide reliable filenames
                allPictureData.put(String.valueOf(pictureData.getIndex()), pictureData);
            }

            for (HSLFSlide slide : slides) {
                // Extract slide title
                String title = getHSLFSlideTitle(slide);
                
                // Extract text content
                StringBuilder textContent = new StringBuilder();
                for (HSLFShape shape : slide.getShapes()) {
                    if (shape instanceof HSLFTextShape) {
                        HSLFTextShape textShape = (HSLFTextShape) shape;
                        String text = textShape.getText();
                        if (text != null && !text.trim().isEmpty() && 
                            (title == null || !text.equals(title))) {
                            textContent.append(text).append("\n\n");
                        }
                    }
                }

                // Extract media files for this slide
                Map<String, File> slideMediaFiles = extractMediaFromHSLFSlide(slide, outputDir, slideNumber, allPictureData);

                // Call the handler with the slide content
                handler.onSlideProcessed(
                    slideNumber,
                    title,
                    textContent.toString().trim(),
                    slideMediaFiles
                );

                slideNumber++;
            }
        }
    }

    private String getSlideTitle(XSLFSlide slide) {
        for (XSLFShape shape : slide.getShapes()) {
            if (shape instanceof XSLFTextShape) {
                XSLFTextShape textShape = (XSLFTextShape) shape;

                // Check if this is likely a title (usually first text shape)
                if (textShape.getText() != null && !textShape.getText().trim().isEmpty()) {
                    return textShape.getText().trim();
                }
            }
        }
        return null;
    }

    private String getHSLFSlideTitle(HSLFSlide slide) {
        for (HSLFShape shape : slide.getShapes()) {
            if (shape instanceof HSLFTextShape) {
                HSLFTextShape textShape = (HSLFTextShape) shape;

                // Check if this is likely a title (usually first text shape)
                if (textShape.getText() != null && !textShape.getText().trim().isEmpty()) {
                    return textShape.getText().trim();
                }
            }
        }
        return null;
    }

    private Map<String, File> extractMediaFromXSLFSlide(XSLFSlide slide, File outputDir, int slideNumber, 
                                                      Map<String, XSLFPictureData> allPictureData) throws IOException {
        Map<String, File> mediaFiles = new HashMap<>();
        
        // Create slide-specific media directory
        File slideMediaDir = new File(outputDir, "slide_" + slideNumber);
        if (!slideMediaDir.exists()) {
            slideMediaDir.mkdirs();
        }

        // Extract pictures from this slide
        int mediaCount = 0;
        for (XSLFShape shape : slide.getShapes()) {
            if (shape instanceof XSLFPictureShape) {
                XSLFPictureShape pictureShape = (XSLFPictureShape) shape;
                XSLFPictureData pictureData = pictureShape.getPictureData();
                
                if (pictureData != null) {
                    PictureType type = pictureData.getType();
                    String extension = type != null ? getExtensionFromPictureType(type) : ".bin";
                    String fileName = "image_" + slideNumber + "_" + (++mediaCount) + extension;
                    File outputFile = new File(slideMediaDir, fileName);

                    try (FileOutputStream fos = new FileOutputStream(outputFile)) {
                        fos.write(pictureData.getData());
                        mediaFiles.put(fileName, outputFile);
                    }
                }
            }
        }

        // Extract embedded objects (videos, audio, etc.)
        try {
            for (POIXMLDocumentPart documentPart : slide.getRelations()) {
                if (documentPart instanceof XSLFObjectData) {
                    XSLFObjectData objData = (XSLFObjectData) documentPart;
                    PackagePart part = objData.getPackagePart();
                    String contentType = part.getContentType();
                    String extension = getExtensionFromContentType(contentType);
                    
                    if (extension != null) {
                        String fileName = "media_" + slideNumber + "_" + (++mediaCount) + extension;
                        File outputFile = new File(slideMediaDir, fileName);
                        
                        try (InputStream is = part.getInputStream();
                             FileOutputStream fos = new FileOutputStream(outputFile)) {
                            byte[] buffer = new byte[8192];
                            int bytesRead;
                            while ((bytesRead = is.read(buffer)) != -1) {
                                fos.write(buffer, 0, bytesRead);
                            }
                            mediaFiles.put(fileName, outputFile);
                        }
                    }
                }
            }
        } catch (Exception e) {
            logger.error("Error extracting embedded objects from slide {}: {}", slideNumber, e.getMessage());
        }

        return mediaFiles;
    }

    private Map<String, File> extractMediaFromHSLFSlide(HSLFSlide slide, File outputDir, int slideNumber,
                                                      Map<String, HSLFPictureData> allPictureData) throws IOException {
        Map<String, File> mediaFiles = new HashMap<>();
        
        // Create slide-specific media directory
        File slideMediaDir = new File(outputDir, "slide_" + slideNumber);
        if (!slideMediaDir.exists()) {
            slideMediaDir.mkdirs();
        }

        // Extract pictures from this slide
        int mediaCount = 0;
        for (HSLFShape shape : slide.getShapes()) {
            if (shape instanceof HSLFPictureShape) {
                HSLFPictureShape pictureShape = (HSLFPictureShape) shape;
                HSLFPictureData pictureData = pictureShape.getPictureData();
                
                if (pictureData != null) {
                    PictureType type = pictureData.getType();
                    String extension = type != null ? getExtensionFromPictureType(type) : ".bin";
                    String fileName = "image_" + slideNumber + "_" + (++mediaCount) + extension;
                    File outputFile = new File(slideMediaDir, fileName);

                    try (FileOutputStream fos = new FileOutputStream(outputFile)) {
                        fos.write(pictureData.getData());
                        mediaFiles.put(fileName, outputFile);
                    }
                }
            }
        }

        // HSLF doesn't have a direct way to extract embedded objects like videos/audio
        // We would need to parse the binary format which is complex

        return mediaFiles;
    }

    private String getExtensionFromPictureType(PictureType type) {
        if (type == null) {
            return ".bin";
        }
        
        switch (type) {
            case JPEG:
                return ".jpg";
            case PNG:
                return ".png";
            case GIF:
                return ".gif";
            case BMP:
                return ".bmp";
            case WMF:
                return ".wmf";
            case EMF:
                return ".emf";
            case PICT:
                return ".pict";
            case DIB:
                return ".dib";
            case TIFF:
                return ".tiff";
            default:
                return ".bin";
        }
    }

    private String getExtensionFromContentType(String contentType) {
        if (contentType == null) return null;

        contentType = contentType.toLowerCase();

        if (contentType.contains("video/mp4")) return ".mp4";
        if (contentType.contains("video/avi")) return ".avi";
        if (contentType.contains("video/mpeg")) return ".mpg";
        if (contentType.contains("video/quicktime")) return ".mov";
        if (contentType.contains("video/x-ms-wmv")) return ".wmv";

        if (contentType.contains("audio/mp3")) return ".mp3";
        if (contentType.contains("audio/mp4")) return ".m4a";
        if (contentType.contains("audio/mpeg")) return ".mp3";
        if (contentType.contains("audio/wav")) return ".wav";
        if (contentType.contains("audio/x-ms-wma")) return ".wma";

        if (contentType.contains("image/")) return ".img";

        return ".bin";  // Default binary extension
    }
} 